import logging
from datetime import datetime

from src.tra_eva.train import train
from src.tra_eva.evaluate import evaluate_accuracy, evaluate_f1
from src.models.supmodel import SupModel


logger = logging.getLogger(__name__)


def sup_singleprocess(model, X_train, y_train, sample_weight=None, X_test=None, y_test=None, evaluate=False, Loss_curve=False):
    """
    @brief 单进程训练和评估监督学习模型

    @param model: SupModel 的子类实例，表示要训练的模型
    @param X_train: 训练特征数据
    @param y_train: 训练标签数据
    @param sample_weight: 样本权重，可选
    @param X_test: 测试特征数据，可选
    @param y_test: 测试标签数据，可选
    @param evaluate: 是否进行评估，默认为 False
    @param Loss_curve: 是否绘制损失曲线，默认为 False

    @return: 字典，包含模型名称、准确率、F1-score、训练时间和损失曲线（如果有）

    @exception TypeError: 如果 model 不是 SupModel 的子类实例
    @exception ValueError: 如果 evaluate=True 时，X_test 或 y_test 为空；或 evaluate=False 时，X_test 和 y_test 不应提供
    """

    # 类型检查
    if not isinstance(model, SupModel):
        raise TypeError("model 必须是 SupModel 的子类实例")

    now = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
    logger.info(f"[{now}] {model.name} 训练开始")

    train_time = train(model, X_train, y_train, sample_weight)

    now = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
    logger.info(f"[{now}] {model.name} 训练结束，用时 {train_time:.2f} 秒")

    # 检查评估参数一致性
    if evaluate and (X_test is None or y_test is None):
        raise ValueError("evaluate=True 时，X_test 和 y_test 不能为空")
    if not evaluate and (X_test is not None or y_test is not None):
        raise ValueError("evaluate=False 时，不应提供 X_test 或 y_test")
    
    # 进行评估
    if evaluate:
        now = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        logger.info(f"[{now}] {model.name} 评估开始")

        y_pred = model.predict(X_test)
        acc = evaluate_accuracy(y_test, y_pred)
        f1 = evaluate_f1(y_test, y_pred)

        now = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        logger.info(f"[{now}] {model.name} 评估结束，准确率 {acc:.4f}，F1-score {f1:.4f}")
    else:
        acc = None
        f1 = None
    
    # 如果需要绘制损失曲线，获取损失属性
    if Loss_curve and hasattr(model, 'loss'):
        loss = model.loss
    else:
        loss = None

    return {'model': model.name, 'accuracy': acc, 'f1': f1, 'train_time': train_time, 'loss': loss}


def sup_process(models: list, X_train, y_train, X_test=None, y_test=None, evaluate=False, Loss_curve=False):
    """
    @brief 使用单进程方式训练和评估多个监督学习模型

    @param models: SupModel 的子类实例列表，表示要训练的模型
    @param X_train: 训练特征数据
    @param y_train: 训练标签数据
    @param X_test: 测试特征数据，可选
    @param y_test: 测试标签数据，可选
    @param evaluate: 是否进行评估，默认为 False
    @param Loss_curve: 是否绘制损失曲线，默认为 False

    @return: 列表，每个元素为字典，包含模型名称、准确率、F1-score、训练时间和损失曲线（如果有）

    @exception TypeError: 如果 models 中的某个元素不是 SupModel 的子类实例
    @exception ValueError: 如果 evaluate=True 时，X_test 或 y_test 为空；或 evaluate=False 时，X_test 和 y_test 不应提供
    """

    results = []
    
    for model in models:
        result = sup_singleprocess(model, X_train, y_train, X_test=X_test, y_test=y_test, evaluate=evaluate, Loss_curve=Loss_curve)
        results.append(result)

    return results